package com.zian.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zian.common.CommonConstant;
import com.zian.context.BaseContext;
import com.zian.domain.dto.*;
import com.zian.domain.po.SysUser;
import com.zian.domain.query.UserQuery;
import com.zian.domain.vo.UserQueryVO;
import com.zian.enums.GenderEnum;
import com.zian.enums.IsDeletedEnum;
import com.zian.enums.LockedEnum;
import com.zian.exception.BadRequestException;
import com.zian.exception.BizIllegalException;
import com.zian.exception.DbException;
import com.zian.mapper.SysUserMapper;
import com.zian.properties.JwtProperties;
import com.zian.service.ISysUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zian.utils.IdUtils;
import com.zian.utils.JwtUtil;
import com.zian.utils.Md5Utils;
import com.zian.utils.RedisUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author
 * @since 2024-09-08
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {

    private final RedisUtils redisUtils;

    private final JwtUtil jwtUtil;

    private final JwtProperties jwtProperties;

    @Override
    public void updateInfo(UpdateUserInfoDTO dto) {
        Long currentId = BaseContext.getCurrentId();
        if (currentId == null) throw new BizIllegalException("用户未登录");
        SysUser user = getById(currentId);
        if (user == null) throw new BizIllegalException("用户不存在");
        user.setGender(GenderEnum.getByCode(dto.getGender()));
        user.setNickName(dto.getNickName());
        user.setIntroduceSign(dto.getIntroduceSign());
        boolean b = updateById(user);
        if (!b) throw new DbException("更新失败");
    }

    @Override
    public void updatePassword(UpdatePwdDTO dto) {
        Long currentId = BaseContext.getCurrentId();
        if (currentId == null) throw new BizIllegalException("用户未登录");
        SysUser user = getById(currentId);
        if (user == null) throw new BizIllegalException("用户不存在");
        if (!Md5Utils.verify(dto.getOldPassword(), user.getPassword())) throw new BadRequestException("旧密码不正确");
        user.setPassword(Md5Utils.encrypt(dto.getNewPassword()));
        boolean b = updateById(user);
        if (!b) throw new DbException("更新失败");
    }

    @Override
    public void logout(HttpServletRequest request) {
        String token = request.getHeader(jwtProperties.getTokenName());

        if (StrUtil.isBlank(token)) return;
        // 清除缓存中的token 和 RefreshToken

        Long currentId = BaseContext.getCurrentId();
        Serializable cacheObject = redisUtils.getCacheObject(CommonConstant.LOGIN_USERID_KEY + currentId);
        if (cacheObject != null){
            // 删除token
            String key =CommonConstant.LOGIN_USER_KEY + (String) cacheObject;
            redisUtils.deleteObject(key);
        }

        // 删除 userId
        redisUtils.deleteObject(CommonConstant.LOGIN_USERID_KEY + currentId);

        BaseContext.removeCurrentId();
    }

    @Override
    public SysUser getInfo() {
        Long currentId = BaseContext.getCurrentId();
        if (currentId == null) throw new BizIllegalException("用户未登录");
        SysUser user = getById(currentId);
        if (user == null) throw new BizIllegalException("用户不存在");
        if (user.getIsDeleted().equals(IsDeletedEnum.YES)) throw new BizIllegalException("用户不存在");
        return user;
    }

    @Override
    public PageDTO<UserQueryVO> queryPage(UserQuery query) {
        Page<SysUser> page = query.toMpPageDefaultSortByCreateTimeDesc();
        lambdaQuery()
                .like(StrUtil.isNotBlank(query.getUsername()), SysUser::getUsername, query.getUsername())
                .eq(query.getGender() != null, SysUser::getGender, query.getGender())
                .eq(query.getLocked() != null, SysUser::getLocked, query.getLocked())
                .page(page);
        List<SysUser> records = page.getRecords();
        if (CollUtil.isEmpty(records)) return PageDTO.empty(0L, 0L);
        List<UserQueryVO> vos = BeanUtil.copyToList(records, UserQueryVO.class);
        return PageDTO.of(page.getTotal(), page.getPages(), vos);
    }

    @Override
    @Transactional
    public void updateLockedByIds(UserLockedDTO dto) {
        if (CollUtil.isEmpty(dto.getIds())) throw new BadRequestException("请选择要操作的用户");
        boolean b = lambdaUpdate()
                .set(SysUser::getLocked, dto.getLocked())
                .in(SysUser::getId, dto.getIds())
                .update();
        if (!b) throw new DbException("操作失败");
    }

    @Override
    public String login(UserLoginDTO dto) {
        //1. 校验验证码
        String cacheCode = (String) redisUtils.getCacheObject(CommonConstant.CAPTCHA_KEY + dto.getUuid());
        redisUtils.deleteObject(CommonConstant.CAPTCHA_KEY + dto.getUuid());
        if (StrUtil.isBlank(cacheCode)) throw new BadRequestException("验证码已过期");
        if (!cacheCode.equals(dto.getCaptcha())) throw new BadRequestException("验证码不正确");

        //2. 查询账号信息
        SysUser one = lambdaQuery().eq(SysUser::getUsername, dto.getUsername()).one();
        if (one == null) throw new BadRequestException("账号不存在");
        if (one.getLocked().equals(LockedEnum.LOCKED)) throw new BizIllegalException("账号已锁定");

        //3. 密码校验
        boolean b = Md5Utils.verify(dto.getPassword(), one.getPassword());
        if (!b) throw new BadRequestException("密码不正确");

        // 设置登录时间
        one.setExpireTime(new Date());
        String token = IdUtils.simpleUUID();

        // 1. token 作为key 用户信息作为 value 存储到 redis 中
        String key = CommonConstant.LOGIN_USER_KEY + token;
        redisUtils.setCacheObject(key, one, jwtProperties.getTtl().intValue(), TimeUnit.MINUTES);
        // 生成token
        String jwt = jwtUtil.createJWT(token);

        // 2. 用户id 作为key ， uuid 作为 value ，来实现单点登录
        String userIdKey = CommonConstant.LOGIN_USERID_KEY + one.getId();
        Serializable cacheObject = redisUtils.getCacheObject(userIdKey);

        //2.1 删除上一个用户的缓存信息
        if (cacheObject!=null){
            String cacheToken = (String) cacheObject;
            // 2.1. 删除 token 的缓存
            redisUtils.deleteObject(CommonConstant.LOGIN_USER_KEY + cacheToken);
            // 2.1. 删除 userId 的缓存
            redisUtils.deleteObject(userIdKey);
        }

        // 2.2. 用户id 作为key ， uuid 作为 value ，来实现单点登录
        redisUtils.setCacheObject(userIdKey, token, jwtProperties.getTtl().intValue(), TimeUnit.MINUTES);


        return jwt;
    }

    @Override
    public void register(UserLoginDTO dto) {
        // 1. 判断用户名是否存在
        SysUser one = lambdaQuery().eq(SysUser::getUsername, dto.getUsername()).one();
        if (one != null) throw new BizIllegalException("用户名已存在");

        //2. 对密码进行加密以及补充其他信息
        SysUser sysUser = BeanUtil.toBean(dto, SysUser.class);
        sysUser.setPassword(Md5Utils.encrypt(dto.getPassword()));
        sysUser.setLocked(LockedEnum.UNLOCKED);
        sysUser.setIsDeleted(IsDeletedEnum.NO);

        //3. 保存用户信息
        boolean b = save(sysUser);
        if (!b) throw new DbException("注册失败");
    }
}
